// programa server cujo objetivo e receber uma solicitacao de arquivo e enviar-lo. 
//@param porto_servidor tam_buffer
//@porto_servidor = porto em que o servidor esta "bind"
//@tam_buffer = tamanho do buffer a ser utilizado

//bibliotecas utilizadas
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

//funcao de auxilio para informar erros e abortar
void error(char *msg)
{
    printf("\n%s\n", msg);
    exit(1);
}
//funcao de auxilio para informar como usar o programa
void usage()
{
	printf("Usage: \n ./client port tam_buffer\n"); 
	printf("\t port: \t\t  Porto do servidor\n"); 
	printf("\t tam_buffer:\t  Tamanho do buffer a ser utilizado\n"); 
} 

int main(int argc, char *argv[])
{
	//verifica numero de argumentos
	if (argc != 3) {
		usage();	
		error("Numero de argumentos invalido");	 
	}
   
	//declaracoes iniciais 
	int sockfd, newsockfd, port, n, clilen;
	struct sockaddr_in serv_addr, cli_addr;
	struct hostent *server;
	char bufferEnv[atoi(argv[2])];
	char buffer[1];
	char arquivo[500]="";
	port = atoi(argv[1]);
	struct timeval time1, time2;
	FILE *file;
	double totalBytes;
    	
	//inicia a contagem do tempo
	gettimeofday(&time1,NULL);

	//cria o novo socket TCP
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0){ 
        	error("ERROR abrindo o socket");
	}


	//define o address family do sockaddr_in 
	serv_addr.sin_family= AF_INET;

	//define o porto a ser utilizado, lembrando de converter antes para network byte order
	serv_addr.sin_port=htons(port);

	//define o endereco do servidor, a constante INADDR_ANY pega este endereco
	serv_addr.sin_addr.s_addr = INADDR_ANY;	
	
	//faz a abertura passiva
	if( bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0 ){
		//imprime as informacoes da conexao
		printf("Server port:\t %d\n", port);
		error("Erro no bind");
	}	

	//permite escutar no socket por conexoes , no max 5
	listen(sockfd,5);

	//faz a abertura passiva
	clilen = sizeof(cli_addr);
	newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
	
	//testa se conseguiu conectar com o client com sucesso
	if ( newsockfd < 0 ){
		error("Problema no accept()");
	}

	//zera o buffer
	bzero(buffer,sizeof(buffer));

	//le o nome do arquivo requerido, byte a byte
	n = read( newsockfd,buffer,1);
	do{
		strncat(arquivo,buffer,1);
		n = read( newsockfd,buffer,1);
	} while ( buffer[0] != '0');

	//tenta abrir o arquivo para leitura
	 file = fopen(arquivo,"r");
	 if ( file != NULL ){
		//tenta enviar um buffer por vez para o cliente	
		n=fread(bufferEnv,1,atoi(argv[2]),file); bufferEnv[n]='\0';
		while ( n > 0){
			write(newsockfd,bufferEnv,n);
			totalBytes+=n;
			n=fread(bufferEnv,1, atoi(argv[2]),file); bufferEnv[n]= '\0';
		}
	 } else{
		error("Nao consegui abrir o arquivo\n");
	}	

	//fecha o arquivo
	fclose(file);

	//termina a contagem do tempo
	gettimeofday(&time2,NULL);

	//imprime o nome do arquivo enviado
	printf("Arquivo solicitado:\t  %s\n", arquivo);
	
	//imprime total de bytes enviados
	printf("Total de Bytes enviados: %d\n", (int) totalBytes);
	


	//imprime o tempo total em microsegundos
	printf("tempo total up: : %f s\n" , (float) ( time2.tv_sec - time1.tv_sec + (time2.tv_usec - time1.tv_usec)/1000000.0) );
}
